use std::{
    collections::HashMap,
    fs::read_to_string,
    ops::{Add, Sub},
};

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
struct Date {
    year: isize,
    month: isize,
    date: isize,
}

impl From<&str> for Date {
    fn from(value: &str) -> Self {
        let mut it = value.split("-");
        Self {
            year: it.next().unwrap().parse().unwrap(),
            month: it.next().unwrap().parse().unwrap(),
            date: it.next().unwrap().parse().unwrap(),
        }
    }
}
impl Add<isize> for Date {
    type Output = Self;

    fn add(self, rhs: isize) -> Self::Output {
        let day = self.count_days();
        Self::from_days(day + rhs)
    }
}
impl Sub<isize> for Date {
    type Output = Self;

    fn sub(self, rhs: isize) -> Self::Output {
        let day = self.count_days();
        Self::from_days(day - rhs)
    }
}
impl Sub<Self> for Date {
    type Output = isize;

    fn sub(self, rhs: Self) -> Self::Output {
        self.count_days() - rhs.count_days()
    }
}
impl Date {
    const MONTHS: [isize; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    /// how many days gone after the 1st January
    fn count_days_this_year(&self) -> isize {
        let mut ans = Self::MONTHS
            .iter()
            .take(self.month as usize - 1)
            .sum::<isize>();
        ans += self.date;
        if is_leap_year(self.year) && self.month > 2 {
            ans += 1;
        }
        ans
    }
    /// how many days gone after the 0001-01-01
    fn count_days(&self) -> isize {
        let mut ans = self.count_days_this_year();
        // calc leap year
        let year = self.year - 1;
        ans += year * 365;
        // leap year
        ans += year / 4;
        ans -= year / 100;
        ans += year / 400;
        ans
    }
    /// what it is after `d` days from 0001-01-01
    fn from_days(mut d: isize) -> Self {
        let (mut year, mut month) = (0, 0);
        for y in 1.. {
            let day = day_of_year(y);
            if d <= day {
                year = y;
                break;
            }
            d -= day;
        }
        for m in 0.. {
            let mut day = Self::MONTHS[m];
            if is_leap_year(year) && m == 1 {
                day += 1;
            }
            if d <= day {
                month = m + 1;
                break;
            }
            d -= day;
        }
        let date = d;

        assert!(month > 0);
        Self {
            year,
            month: month as isize,
            date,
        }
    }
}

/// 0 means Sunday, 1 means Monday, ... 6 means Saturday
#[derive(Debug, Clone, Copy)]
struct Day(isize);

impl From<isize> for Day {
    fn from(value: isize) -> Self {
        Day(value.rem_euclid(7))
    }
}

impl Add<isize> for Day {
    type Output = Self;

    fn add(self, rhs: isize) -> Self::Output {
        ((self.0 + rhs).rem_euclid(7)).into()
    }
}
impl Sub<isize> for Day {
    type Output = Self;

    fn sub(self, rhs: isize) -> Self::Output {
        ((self.0 - rhs).rem_euclid(7)).into()
    }
}

const BASE_DATE: Date = Date {
    year: 1,
    month: 1,
    date: 1,
};
const BASE_DAY: Day = Day(1);

/// store the lunar new years' information
struct Lunar(HashMap<isize, Date>);
impl Lunar {
    /// read data from a local file
    fn build() -> Self {
        let mut map = HashMap::new();
        let str = read_to_string("../../topic1/solutiont3/lunar_newyear").unwrap();
        for l in str.lines() {
            let date: Date = l.into();
            map.insert(date.year, date);
        }
        Self(map)
    }
}

pub fn time_info(time: &str) -> String {
    let date = Date::from(time);
    let next_year = Date {
        month: 12,
        date: 31,
        ..date
    };
    let ans2 = next_year - date;

    let thu = thursday_of(date);
    let rest = thu.count_days_this_year() - 1;
    let ans1: isize = rest / 7 + 1;

    let lunar = Lunar::build();
    let lunar_newyear = *lunar.0.get(&date.year).unwrap();
    let ans3: isize = if lunar_newyear - date > 0 {
        lunar_newyear - date
    } else {
        let lunar_newyear = *lunar.0.get(&(date.year + 1)).unwrap();
        lunar_newyear - date
    } - 1;

    format!("{ans1},{ans2},{ans3}")
}

fn is_leap_year(y: isize) -> bool {
    if y % 400 == 0 {
        true
    } else if y % 100 == 0 {
        false
    } else {
        y % 4 == 0
    }
}

fn day_of_year(y: isize) -> isize {
    if is_leap_year(y) {
        366
    } else {
        365
    }
}

fn thursday_of(date: Date) -> Date {
    let dis = date - BASE_DATE;
    let day = BASE_DAY + dis;

    match day.0 {
        0 => date - 3,
        1..=4 => date + (4 - day.0),
        5..=6 => date - (day.0 - 4),
        _ => panic!(),
    }
}
